#ifndef __BIG_INT__
#define __BIG_INT__

#include <iostream>
#include <vector>
#include <string>
#include <math.h>

// if Strict_Mode is true, any suspicious behavior will result in an error
#define STRICT_MODE 0
#define Bool(logic) logic? 1: 0

class bigint;
int sin(bigint &);
int cos(bigint &);
bigint & tan(bigint &);


class bigint
{
    bool NaN, sign;
	std::vector<unsigned int> data;
    double decimals;
public:
    bigint();
    bigint(int);
    bigint(double);
    bigint(std::string);

    unsigned int size();

    bigint * __add__    (unsigned int);
    bigint * __add__    (double);
    bigint * __add__    (bigint &); // addition
    bigint * __sub__    (unsigned int);
    bigint * __sub__    (double);
    bigint * __sub__    (bigint &); // subtraction
    bigint * __mul__    (int);
    bigint * __mul__    (double);
    bigint * __mul__    (bigint &); // multiplicine
    bigint * __div__    (int);
    bigint * __div__    (double);
    bigint * __div__    (bigint &); // division
    /* Bitwise operation and remainder only support integer part */
    bigint * __mod__    (int);
    bigint * __mod__    (bigint &); // Modulo
    bigint * __and__    (int);
    bigint * __and__    (bigint &);
    bigint * __or__     (int);
    bigint * __or__     (bigint &);
    bigint * __xor__    (int);
    bigint * __xor__    (bigint &);
    bigint * __not__    ();         // Valid only for integer bits
    bigint * __lshift__ (int);
    bigint * __lshift__ (bigint &);
    bigint * __rshift__ (int);
    bigint * __rshift__ (bigint &);
    bigint * __sqrt__   ();
    bigint * __nroot__  (int);
    bigint * __nroot__  (double);   // n-th root, it's not just sqrt.
    bigint * __nroot__  (bigint &);
    bigint * __pow__  (int);
    bigint * __pow__  (double);
    bigint * __pow__  (bigint &);
    bigint * __log__    (int);
    bigint * __log__    (double);
    bigint * __log__    (bigint &);
    bigint * __lg__     ();
    bigint * __ln__     ();
    int    * __sin__    ();
    int    * __cos__    ();
    bigint * __tan__    ();
    bigint * __arcsin__ ();
    bigint * __arccos__ ();
    bigint * __arctan__ ();

    bool __equal__     (int);
    bool __equal__     (double);
    bool __equal__     (bigint &);
    bool __greater__   (int);
    bool __greater__   (double);
    bool __greater__   (bigint &);
    bool __ge__        (int);      // greater equal
    bool __ge__        (double);
    bool __ge__        (bigint &);

    bigint * bin   ();          //Convert a bigint to binary
    std::string    to_string(); //Convert a bigint to string
    int            to_int(unsigned int);
    double         to_double();

    void bit       (unsigned int index);  // Modify the value of a bit in any binary
    void anybit    (unsigned int index);  // Change the value of a bit in any arbitrary base

    friend std::istream & operator >> (std::istream &, bigint &);
    friend std::ostream & operator << (std::ostream &, bigint &);
	bigint * operator =  (int);
    bigint * operator =  (unsigned int);
    bigint * operator =  (double);
    bigint * operator =  (bigint &);
    bigint * operator +  (int);
    bigint * operator +  (double);
    bigint * operator +  (bigint &);
    bigint * operator += (int);
    bigint * operator += (double);
    bigint * operator += (bigint &);
    bigint * operator ++ ();
    bigint * operator -  (int);
    bigint * operator -  (double);
    bigint * operator -  (bigint &);
    bigint * operator -= (int);
    bigint * operator -= (double);
    bigint * operator -= (bigint &);
    bigint * operator -- ();
    bigint * operator *  (int);
    bigint * operator *  (double);
    bigint * operator *  (bigint &);
    bigint * operator *= (int);
    bigint * operator *= (double);
    bigint * operator *= (bigint &);
    bigint * operator /  (int);
    bigint * operator /  (double);
    bigint * operator /  (bigint &);
    bigint * operator /= (int);
    bigint * operator /= (double);
    bigint * operator /= (bigint &);
    bigint * operator %  (int);
    bigint * operator %  (bigint &);
    bigint * operator %= (int);
    bigint * operator %= (bigint &);
    bigint * operator << (int);
    bigint * operator << (bigint &);
    bigint * operator >> (int);
    bigint * operator >> (bigint &);
    bigint * operator ~  ();
    bigint * operator |  (int);
    bigint * operator |  (bigint &);
    bigint * operator &  (int);
    bigint * operator &  (bigint &);
    bool operator <  (int);
    bool operator <  (double);
    bool operator <  (bigint &);
    bool operator >  (int);
    bool operator >  (double);
    bool operator >  (bigint &);
    bool operator <= (int);
    bool operator <= (double);
    bool operator <= (bigint &);
    bool operator >= (int);
    bool operator >= (double);
    bool operator >= (bigint &);
    bool operator == (int);
    bool operator == (double);
    bool operator == (bigint &);

    std::string type();
};


bigint::bigint()
{
    sign = false, NaN = false, decimals = 0;
}

bigint::bigint(int num)
{
    sign = false, decimals = 0;
    if (num < 0) {
        sign = true, num = abs(num);
    }
    NaN = false;
	data.push_back(num);
}

bigint::bigint(double num)
{
    sign = false, NaN = false;
    if (num < 0) {
        sign = true, num = -num;
    }
    // double max: 4503599627370496 = 2 ^ 52
    // so you should use "double - (int)double" carefully.
    // otherwise it will case damages.
    if (num > 2147483647) 
    {
		data;
        unsigned int data1 = num / 4294967296,
                     data0 = num - ((double)data1) * 4294967296;
        data.push_back(data0);
		data.push_back(data1);
		decimals = num - ((double)data1) * 4294967296 - data0;
    } else {
        data.push_back(num);
        decimals = num - data[0];
    }
}

bigint::bigint(std::string num)
{
    /*
        two types of input:
            "12345"
            "12345.678"
    */
    NaN = false, sign = false;
    data.push_back(0);
	decimals = 0;
    if (num[0] == '-') {
        num.erase(num.begin());
        sign = true;
    }

    for (int i = 0; i < num.size(); ++i)
    {
        if ((num[i] < '0' || num[i] > '9') && num[i] != '.')
        {
#if STRICT_MODE
            throw ("error : Invalid character appears!\n");
#else
            goto exit_func;
#endif
        }
        if (num[i] != '.') {
            num[i] -= '0';
        }
    }

    {
		std::string s_integer(num), s_decimal;
		bool dispose_decimals = true;
		int numIndex = num.find(".");
		if (numIndex != -1)
		{
			s_decimal = num;
			s_integer.erase(numIndex);
			s_decimal.erase(0, s_integer.size() + 1);
		} else {
			dispose_decimals = false;
		}

		// dispose s_decimal
		if (dispose_decimals)
		{
			for (unsigned int i = 0; i < s_decimal.size(); ++i) {
				decimals += s_decimal[i] / pow(10, i + 1);
			}
		}

		// dispose s_integer
		unsigned int digit = 0;
		while (s_integer != "")
		{
			if ((digit & 31) == 31) {
				data.push_back(0);
			}
			if (s_integer[s_integer.size() - 1] & 1 == 1) {
				data[digit / 32] |= 1 << (digit & 31);
			}
			++digit;
			// s_integer /= 2
			bool add_i = true, decade = false; // When digit is odd, division needs to record it.
			for (unsigned int i = 0; i < s_integer.size(); )
			{
				if (decade) {
					s_integer[i] += 10;
				}
				if (s_integer[i] & 1 == 1) {
					decade = true;
				} else {
					decade = false;
				}
				s_integer[i] >>= 1;
				if (i == 0 && s_integer[i] == 0) {
					s_integer.erase(s_integer.begin());
					add_i = false;
				} else {
					add_i = true;
				}
				if (add_i) {
					++i;
				}
			}
		}
	}
exit_func:
    // "useless" is just used to avoid errors.
    char useless = 0;
}

unsigned int bigint::size()
{
    return 10 + 4 * (this -> data.size());
}

bigint * bigint::__add__(unsigned int num)
{
	char cin = Bool(this -> data[0] & (1 << 31)) + Bool(num & (1 << 31));
	char cin2= Bool(this -> data[0] & (1 << 30)) && (num & (1 << 30));
	if (cin + cin2 > 1)
	{
		if (data.size() > 1) {
			this -> data[1] += 1;
		} else {
			data.push_back(1);
		}
		num &= ((1 << 31) - 1), this -> data[0] &= ((1 << 31) - 1);
	}
	this -> data[0] += num;
	return this;
	/* // more slower than integer.
	double sum = num + this -> data[0];
	if (sum > 4294967295) {
		this -> data[0] += sum - 4294967296;
		if (dataSize > 1) {
			this -> data[1] += 1;
		} else {
			data = (unsigned int *)realloc(data, ++dataSize * sizeof(int));
		}
	} else {
		this -> data[0] = sum;
	}
	return this;
	*/
}

bigint * bigint::__add__(double num)
{
	return this;
}

bigint * bigint::__add__(bigint & num)
{
	return this;
}

/*
std::string to_string(int num)
{
    if(num == -2147483678) return "-2147483648";
    if(num == 0) return "0";
    bool sign = num < 0? true: false;
    num = abs(num);
    std::string result, mem;
    while(num != 0) { 
        mem.push_back(num % 10 + 0x30);
        result.insert(0, mem); 
        mem.clear(); num /= 10; 
    }
    if(sign) result.insert(0, "-");
    return result;
}
*/

std::string bigint::to_string() //working
{
    std::string result;
    // integer

    // decimals
    return result;
}

int bigint::to_int (unsigned int index = 0)
{
    if (index >= this -> data.size())
    {
#if !STRICT_MODE
        return this -> data[(this -> data.size()) - 1];
#else
        throw ("Error");
#endif
    }
    return this -> data[index];
}

std::ostream & operator << (std::ostream & out, bigint & self) //bug, working
{
	return out;
}

bigint * bigint::operator = (unsigned int num)
{
    this -> data[0] = num;
    return this;
}

std::string bigint::type() //ok
{
    return "bigint";
}

#endif
